<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<style>
html, body {
	margin: 0;
	padding: 0;
	height: 100%;
	overflow: hidden;
}
#video {
	width: 100%;
	height: 100%;
	background: black;
}
</style>
</head>
<body>

<script src="hls.min.js"></script>

<script>

const create = (video) => {
	// always prefer hls.js over native HLS.
	// this is because some Android versions support native HLS
	// but don't support fMP4s.
	if (Hls.isSupported()) {
		const hls = new Hls({
			maxLiveSyncPlaybackRate: 1.5,
		});

		hls.on(Hls.Events.ERROR, (evt, data) => {
			if (data.type === Hls.ErrorTypes.MEDIA_ERROR)
				hls.recoverMediaError();
			else if (data.fatal) {
				hls.destroy();
				setTimeout(() => create(video), 2000);
			}
		});

		hls.on(Hls.Events.MEDIA_ATTACHED, () => {
			hls.loadSource('index.m3u8' + window.location.search);
		});

		hls.on(Hls.Events.MANIFEST_PARSED, () => {
			video.play();
		});

		hls.attachMedia(video);

	} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
		// since it's not possible to detect timeout errors in iOS,
		// wait for the playlist to be available before starting the stream
		fetch('index.m3u8')
			.then(() => {
				video.src = 'index.m3u8';
				video.play();
			});
	}
};

/**
 * Parses the query string from a URL into an object representing the query parameters.
 * If no URL is provided, it uses the query string from the current page's URL.
 *
 * @param {string} [url=window.location.search] - The URL to parse the query string from.
 * @returns {Object} An object representing the query parameters with keys as parameter names and values as parameter values.
 */
const parseQueryString = (url) => {
	const queryString = (url || window.location.search).split("?")[1];
	if (!queryString) return {};

	const paramsArray = queryString.split("&");
	const result = {};

	for (let i = 0; i < paramsArray.length; i++) {
		const param = paramsArray[i].split("=");
		const key = decodeURIComponent(param[0]);
		const value = decodeURIComponent(param[1] || "");

		if (key) {
			if (result[key]) {
				if (Array.isArray(result[key])) {
					result[key].push(value);
				} else {
					result[key] = [result[key], value];
				}
			} else {
				result[key] = value;
			}
		}
	}

	return result;
};

/**
 * Parses a string with boolean-like values and returns a boolean.
 * @param {string} str The string to parse
 * @param {boolean} defaultVal The default value
 * @returns {boolean}
 */
const parseBoolString = (str, defaultVal) => {
	const trueValues = ["1", "yes", "true"];
	const falseValues = ["0", "no", "false"];
	str = (str || "").toString();

	if (trueValues.includes(str.toLowerCase())) {
		return true;
	} else if (falseValues.includes(str.toLowerCase())) {
		return false;
	} else {
		return defaultVal;
	}
};

/**
 * Sets video attributes based on query string parameters or default values.
 *
 * @param {HTMLVideoElement} video - The video element on which to set the attributes.
 */
const setVideoAttributes = (video) => {
	let qs = parseQueryString();

	video.controls = parseBoolString(qs["controls"], true);
	video.muted = parseBoolString(qs["muted"], true);
	video.autoplay = parseBoolString(qs["autoplay"], true);
	video.playsInline = parseBoolString(qs["playsinline"], true);
};

/**
 *
 * @param {(video: HTMLVideoElement) => void} callback
 * @param {HTMLElement} container
 * @returns
 */
const initVideoElement = (callback, container) => {
	return () => {
		const video = document.createElement("video");
		video.id = "video";

		setVideoAttributes(video);
		container.append(video);
		callback(video);
	};
};

window.addEventListener('DOMContentLoaded', initVideoElement(create, document.body));

</script>

</body>
</html>
